#include "udp_socket_io.h"

int read_int(int sockfd, int *n, int flags, struct sockaddr *from, int *fromlen){
  int tmp, num_bytes;
  if((num_bytes=readn(sockfd, &tmp, sizeof(int), flags, from, fromlen))<0)
    return -1;
  *n = (int)ntohl((uint32_t)tmp);
  return num_bytes;
}

int read_string(int sockfd, char *str, int max, int flags, struct sockaddr *from, int *fromlen){
  int num_bytes;
  if((num_bytes=readn(sockfd, str, max*sizeof(char), flags, from, fromlen))<0)
    return -1;
  str[max-1] = '\0';
  return num_bytes;
}

int read_request(int sockfd, request *req, int flags, struct sockaddr *from, int *fromlen){
  int num_bytes;
  void *buffer = (void *)malloc(sizeof(request));
  if(MSG_DONTWAIT & flags!=0){
    if((num_bytes=readn(sockfd, buffer, sizeof(request), flags, from, fromlen))<0){
      free(buffer);
      return -1;
    }
  }else if((num_bytes=recvfrom(sockfd, buffer, sizeof(request), flags, from, fromlen))<0){
    free(buffer);
    return -1;
  }
  req->opt = *(char *)buffer;
  req->id = (int)ntohl(*(uint32_t *)(buffer+sizeof(char)));
  req->nota = (int)ntohl(*(uint32_t *)(buffer+sizeof(char)+sizeof(int)));
  free(buffer);
  return num_bytes;
}

int readn(int sockfd, void *buf, int n, int flags, struct sockaddr *from, int *fromlen){
  int num_bytes, total = n;
  int len;
  fd_set readfds;
  struct timeval timeout;

  if(fromlen)
    len = *fromlen;

  /* Tenta ler do socket */
  do{

    if(fromlen)
      *fromlen = len;

    /* Tenta ler sem bloquear */
    num_bytes=recvfrom(sockfd, buf, n, MSG_DONTWAIT|flags, from, fromlen);
    if(num_bytes<0){

      /* Prepara parametros para chamada de select */
      /* Sets */
      FD_ZERO(&readfds);
      FD_SET(sockfd, &readfds);

      /* Timeout */
      timeout.tv_sec = TIMEOUT_SEC;
      timeout.tv_usec = TIMEOUT_USEC;

      if((flags & MSG_DONTWAIT) != 0 && select(sockfd+1, &readfds, NULL, NULL, &timeout)==0){
	/* Ultima tentativa */
	if((num_bytes=recvfrom(sockfd, buf, n, flags, from, fromlen))<0)
	  return (total-n);
      }else{
	num_bytes=recvfrom(sockfd, buf, n, flags, from, fromlen);
      }
    }
    if(num_bytes<0){
      if(errno!=EINTR){
	return -1;
      }
    }else if(num_bytes==0){
      return (total-n);
    }else{
      buf += num_bytes;
      n -= num_bytes;
    }
  }while(n!=0);
  return total;
}

int write_int(int sockfd, int n, struct sockaddr *to, socklen_t tolen){
  int num_bytes;
  n = htonl((uint32_t)n);
  if((num_bytes=writen(sockfd, &n, sizeof(int), to, tolen))<0)
    return -1;
  return num_bytes;
}

int write_string(int sockfd, char *str, int max, struct sockaddr *to, socklen_t tolen){
  int num_bytes;
  if((num_bytes=writen(sockfd, str, max*sizeof(char), to, tolen))<0)
    return -1;
  return num_bytes;
}

int write_request(int sockfd, request *req, int args, struct sockaddr *to, socklen_t tolen){
  int num_bytes, len = 0;
  void *buffer = (void *)malloc(sizeof(request));

  switch(args){
  case REQ_OPCAO_ID_NOTA:
    len += sizeof(int);
    *(int *)(buffer+sizeof(char)+sizeof(int)) = (int)htonl((uint32_t)req->nota);
  case REQ_OPCAO_ID:
    len += sizeof(int);
    *(int *)(buffer+sizeof(char)) = (int)htonl((uint32_t)req->id);
  case REQ_OPCAO:
    len += sizeof(char);
    *(char *)buffer = req->opt;
    break;
  }
  if((num_bytes=writen(sockfd, buffer, len, to, tolen))<0){
    free(buffer);
    return -1;
  }
  free(buffer);
  return num_bytes;
}

int writen(int sockfd, void *buf, int n, struct sockaddr *to, socklen_t tolen){
  int num_bytes, total = n;
  do{
    num_bytes=sendto(sockfd, buf, n, 0, to, tolen);
    if(num_bytes<0){
      if(errno!=EINTR)
	return -1;
    }else if(num_bytes==0){
      return -1;
    }else{
      buf += num_bytes;
      n -= num_bytes;
    }
  }while(n!=0);
  return total;
}
